package com.topc.oj.language.detail;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.topc.base.utils.LogUtils;
import com.topc.oj.constant.OJConstant;
import com.topc.oj.enums.CodeStatusEnum;
import com.topc.oj.language.LanguageCodeDo;
import com.topc.oj.result.CodeCompileResult;
import com.topc.oj.result.CodeRunResult;
import com.topc.oj.runnable.code.CodeRunnable;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.*;

public class JavaCodeDo implements LanguageCodeDo {

    private ProcessBuilder pb;

    private final static String CLASS_NAME = "Main";

    private final String taskId = UUID.fastUUID().toString();

    //优化每种语言的bug读取器
    @Override
    public CodeStatusEnum checkOutputStream(String s) {
        if (ObjectUtil.isEmpty(s)) return CodeStatusEnum.Finish;
        boolean contains = s.contains("Exception in thread \"main\"");
        if (contains) {
            if (s.contains("java.lang.OutOfMemoryError")) {
                return CodeStatusEnum.MLE;
            }
            return CodeStatusEnum.ERROR;
        }
        return CodeStatusEnum.Finish;
    }

    @Override
    public String taskId() {
        return taskId;
    }

    @Override
    public String fileSuffix() {
        return ".java";
    }

    @Override
    public String className() {
        return CLASS_NAME;
    }

    @Override
    public CodeCompileResult runCompile(String code) {
        pb = new ProcessBuilder("javac", OJConstant.SAVE_FILE_PATH + taskId + "\\" + CLASS_NAME + fileSuffix());
        try {
            Process p = pb.start();
            p.waitFor();
            if (p.exitValue() != 0) {
                InputStream errorStream = p.getErrorStream();
                byte[] buffer = new byte[1024];
                int length;
                StringBuilder errorInfo = new StringBuilder();
                while ((length = errorStream.read(buffer)) != -1) {
                    errorInfo.append(new String(buffer, 0, length));
                }
                String errorInfoS = errorInfo.toString();
                //LogUtils.getLog().error("编译报错 任务id{}", taskId);
                LogUtils.printChar("*", 20);
                //System.out.println("报错内容");
                //System.out.println(errorInfoS);
                LogUtils.printChar("*", 20);
                return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.COMPILE_ERROR).setErrorInfo(errorInfoS);
            }
        } catch (IOException | InterruptedException e) {
            //LogUtils.getLog().info("编译发生未知错误 任务id:{}", taskId);
            return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.UN_KNOW);
        }
        //LogUtils.getLog().info("成功 任务id:{}", taskId);
        return new CodeCompileResult().setCodeStatusEnum(CodeStatusEnum.Finish);
    }

    @Override
    public CodeRunResult run(String input, int timeLimit, int memoryLimit) {
        pb = new ProcessBuilder("java", "-cp", OJConstant.SAVE_FILE_PATH + taskId
                , CLASS_NAME, "-Xmx", String.valueOf(memoryLimit));
        pb.redirectErrorStream(true);
        pb.redirectInput(ProcessBuilder.Redirect.PIPE);
        pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
        pb.environment().put("java.security.manager", "java.lang.SecurityManager");
        pb.environment().put("java.security.policy", "policy.txt");
        ExecutorService executor = Executors.newSingleThreadExecutor();
        CodeRunnable codeRunnable = new CodeRunnable(pb, input, memoryLimit, timeLimit);
        Future<CodeRunResult> future = executor.submit(codeRunnable);
        CodeRunResult codeRunResult = new CodeRunResult();
        try {
            // codeRunResult = future.get(timeLimit, TimeUnit.MILLISECONDS);
            codeRunResult = future.get(2000, TimeUnit.MILLISECONDS); //奶奶滴，给我玩超时滴是吧，直接改吧！
            if (ObjectUtil.isNotEmpty(codeRunResult.getContentInfo())
                    && codeRunResult.getCodeStatusEnum() == CodeStatusEnum.Finish){
                CodeStatusEnum codeStatusEnum = checkOutputStream(codeRunResult.getContentInfo());
                codeRunResult.setCodeStatusEnum(codeStatusEnum);
                if (codeRunResult.getCodeStatusEnum()==CodeStatusEnum.ERROR)
                    codeRunResult.setCodeStatusEnum(codeStatusEnum)
                            .setErrorInfo(codeRunResult.getContentInfo());
                codeRunResult.setCodeStatusEnum(codeStatusEnum);
            }
        } catch (TimeoutException e) {
            codeRunResult.setCodeStatusEnum(CodeStatusEnum.TLE);
        } catch (InterruptedException | ExecutionException e) {
            codeRunResult.setCodeStatusEnum(CodeStatusEnum.UN_KNOW);
        } finally {
            executor.shutdownNow();
        }
        return codeRunResult;
    }
}
